package cn.bearspark.api.service.main.impl;

import cn.bearspark.api.util.ParseableDataUtil;
import cn.bearspark.api.service.main.HappeningService;
import cn.bearspark.api.vo.main.happening.*;
import cn.bearspark.api.vo.main.tag.TagVO;
import cn.bearspark.api.vo.main.user.UserSimpleVO;
import cn.bearspark.api.vo.main.user.UserSlimVO;
import cn.bearspark.api.vo.main.vote.VoteInfo4PublishVO;
import cn.bearspark.common.aop.autoparse.host.AutoParseHost;
import cn.bearspark.common.constant.GlobalConstant;
import cn.bearspark.common.dto.Page;
import cn.bearspark.common.enumeration.ModuleEnum;
import cn.bearspark.common.util.ConvertBeanUtil;
import cn.bearspark.emoji.rpc.EmojiRpc;
import cn.bearspark.forward.rpc.ForwardRpc;
import cn.bearspark.framework.web.starter.context.BSRequestContext;
import cn.bearspark.happening.dto.happening.*;
import cn.bearspark.happening.dto.vote.Option4Publish;
import cn.bearspark.happening.rpc.HappeningRpc;
import cn.bearspark.common.enumeration.LikedEnum;
import cn.bearspark.like.rpc.LikeRpc;
import cn.bearspark.tag.rpc.TagRpc;
import cn.bearspark.user.dto.UserSlimDTO;
import cn.bearspark.user.rpc.UserBaseRpc;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author f
 */
@Service
public class HappeningServiceImpl implements HappeningService {

    /**
     * 默认的侧边栏图片的路径
     */
    private static final String DEFAULT_SIDE_BAR_URL = "http://<h>/bear-spark/file/download/default-side-bar.jpg";

    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private HappeningRpc happeningRpc;
    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private TagRpc tagRpc;
    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private UserBaseRpc userBaseRpc;
    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private EmojiRpc emojiRpc;
    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private LikeRpc likeRpc;
    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private ForwardRpc forwardRpc;

    private final ParseableDataUtil parseableDataUtil;

    public HappeningServiceImpl(ParseableDataUtil parseableDataUtil) {
        this.parseableDataUtil = parseableDataUtil;
    }

    @Override
    public void publish(PublishHappeningReqVO publishHappeningReqVO) {
        long userId = BSRequestContext.getUserId();

        PublishHappeningReqDTO publishHappeningReqDTO =
                ConvertBeanUtil.convert(publishHappeningReqVO, PublishHappeningReqDTO.class);
        publishHappeningReqDTO.setUserId(userId);

        // 转换预约直播的信息
        publishHappeningReqDTO.setBookLiveInfo(ConvertBeanUtil.convert(
                publishHappeningReqVO.getBookLiveInfo(), BookLiveInfo4Publish.class));

        // 转换投票的信息
        VoteInfo4PublishVO voteInfoVO = publishHappeningReqVO.getVoteInfo();
        if (voteInfoVO != null) {
            VoteInfo4Publish voteInfo = ConvertBeanUtil.convert(voteInfoVO, VoteInfo4Publish.class);
            voteInfo.setOptionList(ConvertBeanUtil.convertList(voteInfoVO.getOptionList(), Option4Publish.class));
            publishHappeningReqDTO.setVoteInfo(voteInfo);
        }

        // 发布动态文本
        parseableDataUtil.publish(ModuleEnum.HAPPENING.getCode(),
                () -> happeningRpc.publish(publishHappeningReqDTO), publishHappeningReqDTO);
    }

    @Override
    @AutoParseHost(PrepareRespVO.class)
    public PrepareRespVO prepare() {
        long userId = BSRequestContext.getUserId();

        PrepareRespVO prepareRespVO = new PrepareRespVO();
        prepareRespVO.setFollowerList(ConvertBeanUtil.convertList(
                userBaseRpc.listFSlimInfoWithHappening(userId), UserSlimVO.class));
        prepareRespVO.setRecentTagList(ConvertBeanUtil.convertList(tagRpc.getTop9RecentTag(), TagVO.class));
        prepareRespVO.setSideBarUrl(DEFAULT_SIDE_BAR_URL);
        return prepareRespVO;
    }

    @Override
    public Page<HappeningVO> pageQuery(int pageNum, int pageSize, Long selectedUserId) {
        long userId = BSRequestContext.getUserId();
        return selectedUserId == null ?
                pageQueryByUserId(pageNum, pageSize, userId) :
                pageQueryByFollowerId(pageNum, pageSize, userId, selectedUserId);
    }

    // 分页查询 指定关注者 的动态
    private Page<HappeningVO> pageQueryByFollowerId(int pageNum, int pageSize, long operatorId, long userId) {
        // 分页查询指定关注者的动态信息
        List<HappeningDTO> happeningDTOList = happeningRpc.pageQueryByUserId(operatorId, userId, pageNum, pageSize);
        Page<HappeningDTO> happeningPage = new Page<>();
        happeningPage.setTotal(userBaseRpc.getHappeningNumByUserId(userId));
        happeningPage.setRecords(happeningDTOList);
        int happeningSize = happeningDTOList.size();

        Set<Long> missingUserIdSet = new HashSet<>(happeningSize);
        List<Long> happeningIdList = new ArrayList<>(happeningSize);
        Map<Long, HappeningDTO> happeningDTOMap = new HashMap<>(happeningSize);
        happeningDTOList.forEach(happeningDTO -> {
            Long happeningId = happeningDTO.getHappeningId();
            happeningIdList.add(happeningId);
            happeningDTOMap.put(happeningId, happeningDTO);
            Long happeningUserId = happeningDTO.getUserId();
            if (happeningUserId != null) {
                missingUserIdSet.add(happeningUserId);
            }
        });

        // 将引用的动态加入到动态 DTO 映射中
        Set<Long> quotedHappeningIdSet = new HashSet<>();
        happeningDTOMap.putAll(getQuotedHappeningDTOMap(happeningDTOMap, missingUserIdSet, quotedHappeningIdSet));

        // 如果有缺失的用户最简信息，则查询它们
        Map<Long, UserSlimDTO> userSlimDTOMap = !missingUserIdSet.isEmpty() ?
                userBaseRpc.mapSlimInfo(missingUserIdSet) :
                new HashMap<>();

        return asmHappeningVOPage(happeningDTOMap, quotedHappeningIdSet,
                userSlimDTOMap, happeningIdList, happeningPage, operatorId);
    }

    // 分页查询 所有关注者 和 自己 的动态
    private Page<HappeningVO> pageQueryByUserId(int pageNum, int pageSize, long userId) {
        // 查询用户及其关注者的最简信息，只会返回动态数不为 0 的用户最简信息
        Map<Long, UserSlimDTO> userSlimDTOMap = userBaseRpc.mapSAFSlimInfoWithHappening(userId);
        List<Long> followerIdList;
        {
            Set<Long> userIdSet = new HashSet<>(userSlimDTOMap.keySet());
            // 排除用户自己
            userIdSet.remove(userId);
            followerIdList = userIdSet.stream().toList();
        }

        // 分页查询动态信息
        Page<HappeningDTO> happeningPage = happeningRpc.pageQuerySAF(userId, followerIdList, pageNum, pageSize);
        List<HappeningDTO> happeningDTOList = happeningPage.getRecords();
        int happeningSize = happeningDTOList.size();
        List<Long> happeningIdList = new ArrayList<>(happeningSize);
        Map<Long, HappeningDTO> happeningDTOMap = new HashMap<>(happeningSize);
        happeningDTOList.forEach(happeningDTO -> {
            Long happeningId = happeningDTO.getHappeningId();
            happeningIdList.add(happeningId);
            happeningDTOMap.put(happeningId, happeningDTO);
        });

        // 将引用的动态加入到动态 DTO 映射中
        Set<Long> quotedHappeningIdSet = new HashSet<>();
        Set<Long> missingUserIdSet = new HashSet<>();
        happeningDTOMap.putAll(getQuotedHappeningDTOMap(happeningDTOMap, missingUserIdSet, quotedHappeningIdSet));

        // 如果有缺失的用户最简信息，则查询它们
        if (!missingUserIdSet.isEmpty()) {
            userSlimDTOMap.putAll(userBaseRpc.mapSlimInfo(missingUserIdSet));
        }

        return asmHappeningVOPage(happeningDTOMap, quotedHappeningIdSet,
                userSlimDTOMap, happeningIdList, happeningPage, userId);
    }

    // 组装分页查询动态的信息
    private Page<HappeningVO> asmHappeningVOPage(Map<Long, HappeningDTO> happeningDTOMap,
                                                 Set<Long> quotedHappeningIdSet,
                                                 Map<Long, UserSlimDTO> userSlimDTOMap,
                                                 List<Long> happeningIdList,
                                                 Page<HappeningDTO> happeningDTOPage,
                                                 long userId) {
        // 查询标签信息
        List<Long> tagIdList = happeningDTOMap.values().stream()
                .map(HappeningDTO::getTagId)
                .filter(Objects::nonNull).toList();
        Map<Long, String> tagMap = !CollectionUtils.isEmpty(tagIdList) ?
                tagRpc.getContentMap(tagIdList) :
                new HashMap<>();

        // 组装完整的引用动态信息
        Map<Long, HappeningVO> quotedHappeningMap = quotedHappeningIdSet.stream().collect(Collectors.toMap(
                Function.identity(),
                quotedHappeningId -> asmHappeningVO(happeningDTOMap.get(quotedHappeningId), tagMap, userSlimDTOMap)
        ));

        // 查询该用户是否点赞了这些动态
        Set<Long> likedHappeningIdSet = !happeningIdList.isEmpty() ?
                likeRpc.getLikedHappeningIdSet(happeningIdList, userId) :
                new HashSet<>();

        // 组装完整的动态信息
        List<HappeningInfo> happeningInfoList = new ArrayList<>(happeningIdList.size());
        List<HappeningVO> happeningVOList = happeningIdList.stream().map(happeningId -> {
            HappeningDTO happeningDTO = happeningDTOMap.get(happeningId);

            HappeningVO happeningVO = asmHappeningVO(happeningDTO, tagMap, userSlimDTOMap);

            HappeningInfo happeningInfo = happeningVO.getHappeningInfo();
            happeningInfoList.add(happeningInfo);

            happeningInfo.setLiked(getLikedCode(likedHappeningIdSet.contains(happeningId)));

            HappeningDTO quotedHappeningDTO = happeningDTO.getQuotedHappeningDTO();
            if (quotedHappeningDTO != null) {
                HappeningVO quotedHappening = quotedHappeningMap.get(quotedHappeningDTO.getHappeningId());
                happeningInfoList.add(quotedHappening.getHappeningInfo());
                happeningInfo.setQuotedHappening(quotedHappening);
            }

            return happeningVO;
        }).toList();

        // 反解析所有动态的 content，设置必要的信息
        parseableDataUtil.unparse(ModuleEnum.HAPPENING.getCode(), happeningInfoList);

        Page<HappeningVO> happeningVOPage = new Page<>();
        happeningVOPage.setTotal(happeningDTOPage.getTotal());
        happeningVOPage.setRecords(happeningVOList);
        return happeningVOPage;
    }

    // 查询引用的动态信息、找出缺失的用户信息 并且 获取引用的动态 id
    private Map<Long, HappeningDTO> getQuotedHappeningDTOMap(Map<Long, HappeningDTO> happeningDTOMap,
                                                             Set<Long> missingUserIdSet,
                                                             Set<Long> quotedHappeningIdSet) {
        return happeningDTOMap.values().stream()
                .filter(happeningDTO -> happeningDTO.getQuotedHappeningDTO() != null)
                .map(happeningDTO -> {
                    HappeningDTO quotedHappeningDTO = happeningDTO.getQuotedHappeningDTO();
                    Long quotedUserId = quotedHappeningDTO.getUserId();
                    // 引用的动态可能不是由当前用户关注的人发送的，需要查询这些用户的最简信息
                    if (quotedUserId != null) {
                        missingUserIdSet.add(quotedUserId);
                    }
                    quotedHappeningIdSet.add(quotedHappeningDTO.getHappeningId());
                    return quotedHappeningDTO;
                }).collect(Collectors.toMap(
                        HappeningDTO::getHappeningId,
                        Function.identity(),
                        (happening1, happening2) -> happening1
                ));
    }

    // 组装动态 VO 对象，不包含引用的动态
    private HappeningVO asmHappeningVO(HappeningDTO happeningDTO,
                                       Map<Long, String> tagMap,
                                       Map<Long, UserSlimDTO> userSlimDTOMap) {
        // 组装动态信息
        HappeningInfo happeningInfo = ConvertBeanUtil.convert(happeningDTO, HappeningInfo.class);
        // 获取标签信息
        Long quotedTagId = happeningDTO.getTagId();
        if (quotedTagId != null) {
            happeningInfo.setTag(tagMap.get(quotedTagId));
        }

        // 组装用户信息
        UserSlimVO publisherInfo = new UserSlimVO();
        Long publisherUserId = happeningDTO.getUserId();
        if (publisherUserId != null) {
            UserSlimDTO userSlimDTO = userSlimDTOMap.get(publisherUserId);
            publisherInfo.setUserId(publisherUserId);
            publisherInfo.setUsername(userSlimDTO.getUsername());
            publisherInfo.setAvatarUrl(userSlimDTO.getAvatarUrl());
        }

        // 组装动态 VO 对象
        HappeningVO happeningVO = new HappeningVO();
        happeningVO.setHappeningInfo(happeningInfo);
        happeningVO.setPublisherInfo(publisherInfo);
        return happeningVO;
    }

    @Override
    public HappeningDetailVO detail(long happeningId) {
        long userId = BSRequestContext.getUserId();

        // 获取动态信息
        DetailDTO detailDTO = happeningRpc.detail(userId, happeningId);
        if (detailDTO == null) {
            return null;
        }

        List<Long> tagIdList = new ArrayList<>();
        Set<Long> userIdSet = new HashSet<>();
        {
            Long selectedTagId = detailDTO.getTagId();
            if (selectedTagId != null) {
                tagIdList.add(selectedTagId);
            }
            Long selectedUserId = detailDTO.getUserId();
            if (selectedUserId != null) {
                userIdSet.add(selectedUserId);
            }
        }

        HappeningDTO quotedHappeningDTO = detailDTO.getQuotedHappeningDTO();
        if (quotedHappeningDTO != null) {
            Long selectedTagId = quotedHappeningDTO.getTagId();
            if (selectedTagId != null) {
                tagIdList.add(selectedTagId);
            }
            Long selectedUserId = quotedHappeningDTO.getUserId();
            if (selectedUserId != null) {
                userIdSet.add(selectedUserId);
            }
        }

        // 获取动态中的标签、用户信息
        Map<Long, String> tagContentMap = !tagIdList.isEmpty() ?
                tagRpc.getContentMap(tagIdList) :
                new HashMap<>();
        Map<Long, UserSlimDTO> userSlimDTOMap = !userIdSet.isEmpty() ?
                userBaseRpc.mapSlimInfo(userIdSet) :
                new HashMap<>();

        // 用于反解析的动态信息集合
        List<HappeningInfo> happeningInfoList = new ArrayList<>();

        HappeningInfo happeningInfo = ConvertBeanUtil.convert(detailDTO, HappeningInfo.class);
        UserSlimVO publisherInfo = new UserSlimVO();
        happeningInfoList.add(happeningInfo);

        // 给动态设置 标签信息、用户信息
        {
            Long selectedTagId = detailDTO.getTagId();
            if (selectedTagId != null) {
                happeningInfo.setTag(tagContentMap.get(selectedTagId));
            }
            Long selectedUserId = detailDTO.getUserId();
            if (selectedUserId != null) {
                UserSlimDTO userSlimDTO = userSlimDTOMap.get(selectedUserId);
                publisherInfo.setUserId(selectedUserId);
                publisherInfo.setUsername(userSlimDTO.getUsername());
                publisherInfo.setAvatarUrl(userSlimDTO.getAvatarUrl());
            }
        }

        // 给引用的动态设置标签、用户信息
        if (quotedHappeningDTO != null) {
            HappeningVO quotedHappening = asmHappeningVO(quotedHappeningDTO, tagContentMap, userSlimDTOMap);
            happeningInfo.setQuotedHappening(quotedHappening);
            happeningInfoList.add(quotedHappening.getHappeningInfo());
        }

        // 查询用户是否点赞了该动态
        happeningInfo.setLiked(getLikedCode(likeRpc.isHappeningLiked(happeningId, userId)));

        HappeningDetailVO happeningDetailVO = new HappeningDetailVO();
        happeningDetailVO.setHappeningInfo(happeningInfo);
        happeningDetailVO.setPublisherInfo(publisherInfo);

        // 反解析所有的 content，设置必要的信息
        parseableDataUtil.unparse(ModuleEnum.HAPPENING.getCode(), happeningInfoList);

        return happeningDetailVO;
    }

    // 获取点赞的枚举值
    private int getLikedCode(boolean happeningLiked) {
        return happeningLiked ?
                LikedEnum.LIKED.getCode() :
                LikedEnum.UNLIKED.getCode();
    }

    @Override
    public void delete(Long happeningId) {
        long userId = BSRequestContext.getUserId();

        // 删除 动态数据
        happeningRpc.delete(userId, happeningId);

        // 删除 动态带有的其它数据
        parseableDataUtil.afterDeleteBase(ModuleEnum.HAPPENING.getCode(), happeningId);
    }

    @Override
    public void modify(ModifyHappeningVO modifyHappeningVO) {
        long userId = BSRequestContext.getUserId();

        ModifyReqDTO modifyReqDTO = ConvertBeanUtil.convert(modifyHappeningVO, ModifyReqDTO.class);
        modifyReqDTO.setUserId(userId);
        happeningRpc.modify(modifyReqDTO);
    }

    @Override
    public FollowerListVO followerList() {
        long userId = BSRequestContext.getUserId();

        FollowerListVO followerListVO = new FollowerListVO();
        followerListVO.setFollowerList(
                ConvertBeanUtil.convertList(userBaseRpc.listFSimpleInfo(userId), UserSimpleVO.class));
        return followerListVO;
    }

    @Override
    public EmojiListVO emojiList() {
        EmojiListVO emojiListVO = new EmojiListVO();
        emojiListVO.setEmojiUrlList(emojiRpc.listAllEmojis());
        return emojiListVO;
    }

    @Override
    public Page<UserSlimVO> pageQueryLikedList(Integer pageNum, Integer pageSize, Long happeningId) {
        Page<Long> userIdPage = new Page<>();
        userIdPage.setTotal(happeningRpc.getLikeNum(happeningId));
        userIdPage.setRecords(likeRpc.pageHappeningLikedUserId(pageNum, pageSize, happeningId));
        return asmUserSlimVOPage(userIdPage);
    }

    @Override
    public Page<UserSlimVO> pageQueryForwardedList(Integer pageNum, Integer pageSize, Long happeningId) {
        Page<Long> userIdPage = new Page<>();
        userIdPage.setTotal(happeningRpc.getForwardNum(happeningId));
        userIdPage.setRecords(forwardRpc.pageHappeningForwardedUserId(pageNum, pageSize, happeningId));
        return asmUserSlimVOPage(userIdPage);
    }

    // 组装用户最简信息的分页查询对象
    private Page<UserSlimVO> asmUserSlimVOPage(Page<Long> userIdPage) {
        List<Long> userIdList = userIdPage.getRecords();
        Map<Long, UserSlimDTO> userSlimDTOMap = userBaseRpc.mapSlimInfo(new HashSet<>(userIdList));
        Page<UserSlimVO> userSlimVOPage = new Page<>();
        userSlimVOPage.setTotal(userIdPage.getTotal());
        userSlimVOPage.setRecords(userIdList.stream().map(
                userId -> ConvertBeanUtil.convert(userSlimDTOMap.get(userId), UserSlimVO.class)
        ).toList());
        return userSlimVOPage;
    }

}
